// sdlock.c
#include <SDL3/SDL.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <security/pam_appl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/mman.h>
#include <time.h>
// 2560x1600
#define WINDOW_WIDTH 2560
#define WINDOW_HEIGHT 1600
#define PADDING 15
#define FONT_SIZE 36
#define HINT_FONT_SIZE 20
#define FONT_PATH "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf"
#define MAX_PASSWORD_LEN 512
#define MAXFailed_ATTEMPTS 5

#define COLOR_BG 0, 0, 0, 255
#define COLOR_INPUT_BG 50, 50, 50, 255
#define COLOR_TEXT 255, 255, 255, 255
#define COLOR_PROMPT 255, 204, 0, 255
#define COLOR_ERROR 255, 0, 0, 255
#define COLOR_SUCCESS 0, 255, 0, 255
#define COLOR_SEPARATOR 100, 100, 100, 255

char password[MAX_PASSWORD_LEN] = {0};
size_t cursor = 0;
int input_len = 0;
int failed_attempts = 0;
bool is_locked = true;
bool show_help = false;

SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
FT_Library ft;
FT_Face main_face;
FT_Face hint_face;
SDL_Texture *main_glyphs[128] = {NULL};
SDL_Texture *hint_glyphs[128] = {NULL};
#define EMPTY_GLYPH_SENTINEL (SDL_Texture *)-1

// Fixed: Corrected function name (removed duplicate "conv_callback" and extraneous "callback")
static int pam_conv_callback(int num_msg, const struct pam_message **msg,
                             struct pam_response **resp, void *appdata_ptr)
{
    (void)appdata_ptr;
    *resp = calloc(num_msg, sizeof(struct pam_response));
    if (!*resp)
        return PAM_BUF_ERR;

    for (int i = 0; i < num_msg; i++)
    {
        if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
        {
            resp[i]->resp = strdup(password);
            resp[i]->resp_retcode = 0;
        }
    }
    return PAM_SUCCESS;
}

static bool verify_password()
{
    if (input_len == 0)
        return false;

    struct pam_conv conv = {pam_conv_callback, NULL}; // Now correctly references the fixed function name
    pam_handle_t *pam_handle = NULL;
    const char *username = getlogin();
    int ret = pam_start("login", username, &conv, &pam_handle);

    if (ret != PAM_SUCCESS)
    {
        fprintf(stderr, "PAM init failed: %s\n", pam_strerror(pam_handle, ret));
        pam_end(pam_handle, ret);
        return false;
    }

    ret = pam_authenticate(pam_handle, 0);
    pam_end(pam_handle, ret);

    if (ret == PAM_SUCCESS)
        return true;
    failed_attempts++;
    return false;
}

static void clear_password()
{
    memset(password, 0, sizeof(password));
    input_len = 0;
    cursor = 0;
}

int get_main_string_width(const char *str)
{
    int width = 0;
    for (int i = 0; str[i]; i++)
    {
        FT_Load_Char(main_face, str[i], FT_LOAD_DEFAULT);
        width += main_face->glyph->advance.x >> 6;
    }
    return width;
}

int get_hint_string_width(const char *str)
{
    int width = 0;
    for (int i = 0; str[i]; i++)
    {
        FT_Load_Char(hint_face, str[i], FT_LOAD_DEFAULT);
        width += hint_face->glyph->advance.x >> 6;
    }
    return width;
}

bool load_glyphs(FT_Face face, SDL_Texture **glyphs, int size)
{
    FT_Set_Pixel_Sizes(face, 0, size);

    for (unsigned char c = 32; c <= 126; c++)
    {
        if (FT_Load_Char(face, c, FT_LOAD_RENDER))
        {
            fprintf(stderr, "Warning: Missing glyph '%c' (size: %d)\n", c, size);
            return false;
        }

        FT_Bitmap *bitmap = &face->glyph->bitmap;
        if (bitmap->width == 0 || bitmap->rows == 0)
        {
            glyphs[c] = EMPTY_GLYPH_SENTINEL;
            continue;
        }

        SDL_Surface *surface = SDL_CreateSurface(
            bitmap->width, bitmap->rows, SDL_PIXELFORMAT_RGBA32);
        if (!surface)
        {
            fprintf(stderr, "SDL surface failed for glyph '%c'\n", c);
            return false;
        }

        SDL_FillSurfaceRect(surface, NULL, 0x00000000);
        Uint32 *pixels = (Uint32 *)surface->pixels;
        for (int y = 0; y < bitmap->rows; y++)
        {
            for (int x = 0; x < bitmap->width; x++)
            {
                Uint8 alpha = bitmap->buffer[y * bitmap->pitch + x];
                if (alpha > 0)
                {
                    pixels[y * surface->w + x] = 0xFFFFFFFF | (alpha << 24);
                }
            }
        }

        glyphs[c] = SDL_CreateTextureFromSurface(renderer, surface);
        if (!glyphs[c])
        {
            fprintf(stderr, "SDL texture failed for glyph '%c'\n", c);
            SDL_DestroySurface(surface);
            return false;
        }
        SDL_SetTextureBlendMode(glyphs[c], SDL_BLENDMODE_BLEND);
        SDL_DestroySurface(surface);
    }
    return true;
}

bool init_freetype()
{
    if (FT_Init_FreeType(&ft))
    {
        fprintf(stderr, "FreeType initialization failed\n");
        return false;
    }

    if (FT_New_Face(ft, FONT_PATH, 0, &main_face))
    {
        fprintf(stderr, "Failed to load main font: %s\n", FONT_PATH);
        FT_Done_FreeType(ft);
        return false;
    }

    if (FT_New_Face(ft, FONT_PATH, 0, &hint_face))
    {
        fprintf(stderr, "Failed to load hint font: %s\n", FONT_PATH);
        FT_Done_Face(main_face);
        FT_Done_FreeType(ft);
        return false;
    }

    if (!load_glyphs(main_face, main_glyphs, FONT_SIZE))
    {
        FT_Done_Face(hint_face);
        FT_Done_Face(main_face);
        FT_Done_FreeType(ft);
        return false;
    }

    if (!load_glyphs(hint_face, hint_glyphs, HINT_FONT_SIZE))
    {
        FT_Done_Face(hint_face);
        FT_Done_Face(main_face);
        FT_Done_FreeType(ft);
        return false;
    }

    return true;
}

void draw_main_char(int x, int y, char c)
{
    if (c < 32 || c > 126)
        return;
    if (main_glyphs[c] == EMPTY_GLYPH_SENTINEL)
        return;
    if (!main_glyphs[c])
        return;

    FT_Load_Char(main_face, c, FT_LOAD_DEFAULT);
    int x_off = main_face->glyph->bitmap_left;
    int y_off = main_face->glyph->bitmap_top;

    SDL_FRect dst = {
        (float)(x + x_off),
        (float)(y + (FONT_SIZE - y_off)),
        (float)main_face->glyph->bitmap.width,
        (float)main_face->glyph->bitmap.rows};
    SDL_RenderTexture(renderer, main_glyphs[c], NULL, &dst);
}

int draw_main_string(int x, int y, const char *str)
{
    int start_x = x;
    for (int i = 0; str[i]; i++)
    {
        draw_main_char(x, y, str[i]);
        FT_Load_Char(main_face, str[i], FT_LOAD_DEFAULT);
        x += main_face->glyph->advance.x >> 6;
    }
    return x - start_x;
}

void draw_hint_char(int x, int y, char c)
{
    if (c < 32 || c > 126)
        return;
    if (hint_glyphs[c] == EMPTY_GLYPH_SENTINEL)
        return;
    if (!hint_glyphs[c])
        return;

    FT_Load_Char(hint_face, c, FT_LOAD_DEFAULT);
    int x_off = hint_face->glyph->bitmap_left;
    int y_off = hint_face->glyph->bitmap_top;

    SDL_FRect dst = {
        (float)(x + x_off),
        (float)(y + (HINT_FONT_SIZE - y_off)),
        (float)hint_face->glyph->bitmap.width,
        (float)hint_face->glyph->bitmap.rows};
    SDL_RenderTexture(renderer, hint_glyphs[c], NULL, &dst);
}

int draw_hint_string(int x, int y, const char *str)
{
    int start_x = x;
    for (int i = 0; str[i]; i++)
    {
        draw_hint_char(x, y, str[i]);
        FT_Load_Char(hint_face, str[i], FT_LOAD_DEFAULT);
        x += hint_face->glyph->advance.x >> 6;
    }
    return x - start_x;
}

void handle_input()
{
    SDL_Event event;
    bool ctrl_pressed = false;

    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
        case SDL_EVENT_QUIT:
            is_locked = false;
            break;

        case SDL_EVENT_KEY_DOWN:
            ctrl_pressed = (event.key.mod & SDL_KMOD_CTRL) != 0;

            if (event.key.key == SDLK_F1)
            {
                show_help = !show_help;
                return;
            }

            if (event.key.key == SDLK_ESCAPE)
            {
                clear_password();
                return;
            }

            if (event.key.key == SDLK_RETURN)
            {
                if (verify_password())
                {
                    is_locked = false;
                }
                else
                {
                    clear_password();
                }
                return;
            }

            if ((event.key.key == SDLK_BACKSPACE || (ctrl_pressed && event.key.key == SDLK_H)) && cursor > 0)
            {
                cursor--;
                memmove(&password[cursor], &password[cursor + 1], MAX_PASSWORD_LEN - cursor - 1);
                input_len--;
                return;
            }

            if ((event.key.key == SDLK_DELETE || (ctrl_pressed && event.key.key == SDLK_D)) && cursor < (size_t)input_len)
            {
                memmove(&password[cursor], &password[cursor + 1], MAX_PASSWORD_LEN - cursor - 1);
                input_len--;
                return;
            }

            if (ctrl_pressed && event.key.key == SDLK_K)
            {
                password[cursor] = '\0';
                input_len = cursor;
                return;
            }

            if (ctrl_pressed && event.key.key == SDLK_U)
            {
                clear_password();
                return;
            }

            if (event.key.key == SDLK_RIGHT || (ctrl_pressed && event.key.key == SDLK_F))
            {
                if (cursor < (size_t)input_len)
                    cursor++;
                return;
            }
            if (event.key.key == SDLK_LEFT || (ctrl_pressed && event.key.key == SDLK_B))
            {
                if (cursor > 0)
                    cursor--;
                return;
            }

            if (event.key.key == SDLK_HOME || (ctrl_pressed && event.key.key == SDLK_A))
            {
                cursor = 0;
                return;
            }

            if (event.key.key == SDLK_END || (ctrl_pressed && event.key.key == SDLK_E))
            {
                cursor = input_len;
                return;
            }
            break;

        case SDL_EVENT_TEXT_INPUT:
            if (input_len < MAX_PASSWORD_LEN - 1 &&
                event.text.text[0] >= 32 && event.text.text[0] <= 126)
            {
                memmove(&password[cursor + 1], &password[cursor], MAX_PASSWORD_LEN - cursor - 1);
                password[cursor++] = event.text.text[0];
                input_len++;
            }
            break;
        }
    }
}

void render()
{
    SDL_SetRenderDrawColor(renderer, COLOR_BG);
    SDL_RenderClear(renderer);

    const char *title = "Screen Locked";
    int title_x = (WINDOW_WIDTH - get_main_string_width(title)) / 2;
    draw_main_string(title_x, PADDING, title);

    int input_y = PADDING + FONT_SIZE + 20;
    SDL_FRect input_rect = {
        (float)PADDING,
        (float)input_y,
        (float)(WINDOW_WIDTH - 2 * PADDING),
        (float)(FONT_SIZE + 12)};
    SDL_SetRenderDrawColor(renderer, COLOR_INPUT_BG);
    SDL_RenderFillRect(renderer, &input_rect);

    const char *prompt = "Password: ";
    int prompt_x = PADDING + 8;
    SDL_SetRenderDrawColor(renderer, COLOR_PROMPT);
    draw_main_string(prompt_x, input_y + 6, prompt);
    int prompt_width = get_main_string_width(prompt);

    char pass_indicator[MAX_PASSWORD_LEN + 1] = {0};
    memset(pass_indicator, '*', input_len);
    SDL_SetRenderDrawColor(renderer, COLOR_SUCCESS);
    draw_main_string(prompt_x + prompt_width, input_y + 6, pass_indicator);

    static Uint32 last_cursor_flip = 0;
    static bool cursor_visible = true;
    Uint32 now = SDL_GetTicks();
    if (now - last_cursor_flip > 500)
    {
        cursor_visible = !cursor_visible;
        last_cursor_flip = now;
    }
    if (cursor_visible)
    {
        int cursor_x = prompt_x + prompt_width;
        for (size_t i = 0; i < cursor; i++)
        {
            FT_Load_Char(main_face, pass_indicator[i], FT_LOAD_DEFAULT);
            cursor_x += main_face->glyph->advance.x >> 6;
        }
        SDL_FRect cursor_rect = {
            (float)cursor_x,
            (float)(input_y + 6),
            2.0f,
            (float)FONT_SIZE};
        SDL_SetRenderDrawColor(renderer, COLOR_TEXT);
        SDL_RenderFillRect(renderer, &cursor_rect);
    }

    int separator_y = input_y + FONT_SIZE + 20;
    SDL_SetRenderDrawColor(renderer, COLOR_SEPARATOR);
    SDL_RenderLine(renderer,
                   (float)PADDING, (float)separator_y,
                   (float)(WINDOW_WIDTH - PADDING), (float)separator_y);

    if (failed_attempts > 0)
    {
        char fail_text[64];
        snprintf(fail_text, sizeof(fail_text), "Failed: %d/%d", failed_attempts, MAXFailed_ATTEMPTS);
        int fail_x = (WINDOW_WIDTH - get_main_string_width(fail_text)) / 2;
        SDL_SetRenderDrawColor(renderer, COLOR_ERROR);
        draw_main_string(fail_x, separator_y + 15, fail_text);

        if (failed_attempts >= MAXFailed_ATTEMPTS)
        {
            const char *block_text = "Temporarily Blocked";
            int block_x = (WINDOW_WIDTH - get_main_string_width(block_text)) / 2;
            draw_main_string(block_x, separator_y + 15 + FONT_SIZE, block_text);
        }
    }

    if (show_help)
    {
        const char *help[] = {
            "ESC: Clear | Enter: Submit | F1: Hide Help",
            "Ctrl+A: Home | Ctrl+E: End | Ctrl+F/B: Left/Right",
            "Backspace: Delete | Ctrl+D: Delete Forward | Ctrl+K: Clear Rest"};
        int help_y = separator_y + 40;
        for (int i = 0; i < 3; i++)
        {
            int help_x = (WINDOW_WIDTH - get_hint_string_width(help[i])) / 2;
            draw_hint_string(help_x, help_y + i * (HINT_FONT_SIZE + 10), help[i]);
        }
    }
    else
    {
        const char *hint = "Press F1 for help";
        int hint_x = WINDOW_WIDTH - PADDING - get_hint_string_width(hint) - 5;
        draw_hint_string(hint_x, WINDOW_HEIGHT - PADDING - HINT_FONT_SIZE, hint);
    }

    SDL_RenderPresent(renderer);
}

void cleanup()
{
    memset(password, 0, sizeof(password));

#ifdef __linux__
    munlock(password, sizeof(password));
#endif

    for (int c = 32; c <= 126; c++)
    {
        if (main_glyphs[c] && main_glyphs[c] != EMPTY_GLYPH_SENTINEL)
            SDL_DestroyTexture(main_glyphs[c]);
        if (hint_glyphs[c] && hint_glyphs[c] != EMPTY_GLYPH_SENTINEL)
            SDL_DestroyTexture(hint_glyphs[c]);
    }

    FT_Done_Face(hint_face);
    FT_Done_Face(main_face);
    FT_Done_FreeType(ft);

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

int main()
{
#ifdef __linux__
    if (mlock(password, sizeof(password)) != 0)
    {
        fprintf(stderr, "Warning: Could not lock password memory (run with sufficient privileges)\n");
    }
#endif

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0)
    {
        fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
        return 1;
    }

    int display_count = 0;
    SDL_DisplayID *displays = SDL_GetDisplays(&display_count);
    SDL_DisplayID main_display = (display_count > 0) ? displays[0] : 0;
    SDL_free(displays);

    const SDL_DisplayMode *dm = SDL_GetCurrentDisplayMode(main_display);
    if (!dm)
    {
        fprintf(stderr, "Failed to get display mode: %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    window = SDL_CreateWindow(
        "sdlock",
        WINDOW_WIDTH,
        WINDOW_HEIGHT,
        SDL_WINDOW_BORDERLESS);
    if (!window)
    {
        fprintf(stderr, "Window creation failed: %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    int window_x = (dm->w - WINDOW_WIDTH) / 2;
    int window_y = (dm->h - WINDOW_HEIGHT) / 2;
    SDL_SetWindowPosition(window, window_x, window_y);

    renderer = SDL_CreateRenderer(window, NULL);
    if (!renderer)
    {
        fprintf(stderr, "Renderer creation failed: %s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    SDL_StartTextInput(window);

    if (!init_freetype())
    {
        cleanup();
        return 1;
    }

    while (is_locked)
    {
        handle_input();
        render();
        SDL_Delay(16);
    }

    cleanup();
    return 0;
}
